#include <fstream>
#include "mainfrm.h"
#include "resource.h"
#include "regwrap.h"
#include "capicon.h"
#include "dialogs.h"
#include <boost/iostreams/stream.hpp>

using namespace std;

extern const char cszRegKey[] = "HKLM\\Software\\Novavox\\Capicon";

streamsize wbuf::write( const char *sz, streamsize n )
{
    if ( !::IsWindow(m_pedit->m_hWnd) )
        return 0;

    const char *p = sz;
    const char *pend = sz+n;

    string s( n*2, 0 );

    for ( int i = 0; p < pend; ++i, ++p ) {
        if ( *p == '\n' )
            s[i++] = '\r';
        s[i] = *p;
    }

    m_pedit->SetSel( -1, -1 /* m_prichedit->GetTextLength(), m_prichedit->GetTextLength() */ );
    m_pedit->ReplaceSel( s.c_str() );

    return n;
}

BEGIN_MESSAGE_MAP(CCapiConApp, CWinApp)
    //{{AFX_MSG_MAP(CCapiConApp)
    ON_COMMAND(ID_APP_ABOUT, OnAppAbout)
    ON_COMMAND(ID_EDIT_CUT, OnEditCut)
    //}}AFX_MSG_MAP
END_MESSAGE_MAP()

void CCapiConApp::CallBack( const _API_MSG& msg )
{
    if ( msg.header.command != _INFO_I &&
         msg.header.command != _DISCONNECT_I &&
         msg.header.command != _FACILITY_I &&
         msg.header.command != (_CONNECT_R | CONFIRM) )
        return;

    CListCtrl& bcList = *((CMainFrame*)m_pMainWnd)->m_pBCList;

    char buf[2048];

    for ( word i = 0; i < wBNumber; ++i )
    {
        word wActivePLCI = 0;

        typedef vector<word> WordVector;
        WordVector awHeldPLCIs;

        for ( CAPI::ConnectionMap::iterator p = pcapi->conns.begin(); p != pcapi->conns.end(); ++p )
        {
            if ( p->second->GetBChannel() != i )
                continue;

            if ( p->second->GetState() == Connection::stHeld )
                awHeldPLCIs.push_back( p->second->GetPLCI() );
            else {
                if ( wActivePLCI != 0 )
                    ::AfxMessageBox( "Two PLCIs on one B channel!", MB_OK | MB_ICONEXCLAMATION );
                wActivePLCI = p->second->GetPLCI();
            }
        }

        strcpy( buf, awHeldPLCIs.empty() ? "-" : "" );

        if ( wActivePLCI != 0 )
            sprintf( buf, "%02d", wActivePLCI );

        for ( WordVector::iterator q = awHeldPLCIs.begin(); q != awHeldPLCIs.end(); ++q ) {
            if ( wActivePLCI != 0 || q != awHeldPLCIs.begin() )
                strcat( buf, " " );
            sprintf( buf+strlen(buf), "(%02d)", *q );
        }

        bcList.SetItem( i, 1, LVIF_TEXT, buf, 0, 0, 0, 0 );
    }
}

BOOL CCapiConApp::InitInstance()
{
    m_pMainWnd = new CMainFrame;

    ((CFrameWnd*)m_pMainWnd)->LoadFrame( IDR_MAINFRAME, WS_OVERLAPPEDWINDOW | FWS_ADDTOTITLE, NULL, NULL );
    m_pMainWnd->ShowWindow( SW_SHOW );
    m_pMainWnd->UpdateWindow();

    aDtmf = (unsigned char *)::LockResource(::LoadResource(0,::FindResource(0,"DTMFData","RAW")));
    nDtmfBufSize = 23040;

    // Flipping the DTMF wave data

    for ( unsigned long i = 0; i < nDtmfBufSize; ++i )
        aDtmf[i] = flip_byte[aDtmf[i]];

    // Create the RichEdit logout stream

    apws = auto_ptr<ostream>( new ostream( new boost::iostreams::stream_buffer<wbuf>( ((CMainFrame*)m_pMainWnd)->GetLogControl() ) ) );

    *szOwnNumber = '\0';
    dword dwController = _cpp_max<int>( atoi(m_lpCmdLine), 1 );
    bool bAutoConnectResponse = RegWrap( cszRegKey ).ReadDword( "bAutoAccept", 1 ) != 0;
    bool bAutoAnswer = RegWrap( cszRegKey ).ReadDword( "bAutoAnswer", 1 ) != 0;

    pcapi = new CAPI( dwController, 0x5FF, szOwnNumber, bAutoAnswer, bAutoConnectResponse, 256, plogstream(), &StaticCallBack, this );

    CAPIControllerProfile *p = pcapi->ControllerProfiles.size() >= dwController ? &(pcapi->ControllerProfiles[dwController-1]) : 0;
    wBNumber = p ? p->wNumberOfBChannels + 1 + p->wNumberOfBChannels/30 : 0;
    CListCtrl& bcList = *((CMainFrame*)m_pMainWnd)->m_pBCList;
    char buf[2048];

    DWORD dwStyle = ::GetWindowLong( bcList, GWL_STYLE );
    ::SetWindowLong( bcList, GWL_STYLE, dwStyle & ~LVS_TYPEMASK | LVS_REPORT );

    for ( int i = 0; i < wBNumber; ++i ) {
        sprintf( buf, "%02d", i );
        bcList.InsertItem( i, buf );
        bcList.SetItem( i, 1, LVIF_TEXT, "-", 0, 0, 0, 0 );
    }

    logout( plogstream(), "\nManufacturer:         %s\n",    pcapi->szManufacturer );
    logout( plogstream(), "Serial Number:        %s\n",      pcapi->szSerialNumber );
    logout( plogstream(), "CAPI Version:         %lu.%lu\n", pcapi->dwCAPIMajor, pcapi->dwCAPIMinor );
    logout( plogstream(), "Manufacturer Version: %lu.%lu\n", pcapi->dwManufacturerMajor, pcapi->dwManufacturerMinor );
    logout( plogstream(), "# of Controllers:     %hu\n\n",   pcapi->wNumberOfControllers );

    pcapi->Listen( 0x5ff, 1 /* Any match */ );

    return TRUE;
}

CCapiConApp::~CCapiConApp()
{
    delete pcapi;
}

class CAboutDlg : public CDialog
{
public:
    CAboutDlg();

    //{{AFX_DATA(CAboutDlg)
    enum { IDD = IDD_ABOUTBOX };
    //}}AFX_DATA

protected:
    //{{AFX_VIRTUAL(CAboutDlg)
    virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
    //}}AFX_VIRTUAL

protected:
    //{{AFX_MSG(CAboutDlg)
    //}}AFX_MSG
    DECLARE_MESSAGE_MAP()
};

CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
    //{{AFX_DATA_INIT(CAboutDlg)
    //}}AFX_DATA_INIT
}

void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
    CDialog::DoDataExchange(pDX);
    //{{AFX_DATA_MAP(CAboutDlg)
    //}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
    //{{AFX_MSG_MAP(CAboutDlg)
    //}}AFX_MSG_MAP
END_MESSAGE_MAP()

void CCapiConApp::OnAppAbout()
{
    CAboutDlg aboutDlg;
    aboutDlg.DoModal();
}

CCapiConApp theApp;

void ConstructBChannelInfo( unsigned char *memBuf, bool bIsBRI, word nBChannelNumber, bool bPreferred )
{
    if ( bIsBRI )
        sprintf( (char*)memBuf, "\x01%c", (bPreferred ? 0x80 : 0x88) | nBChannelNumber&0x03 );
    else
        sprintf( (char*)memBuf, "\x03%c\x83%c", bPreferred ? 0xA1 : 0xA9, nBChannelNumber | 0x80 );
}

void CCapiConApp::CmdLine( const string& sCmdLine )
{
    const dword nMaxNumberOfParams = 6;         // Maximum number of params user can pass to a command
    char cCommand = '\0';                       // Command extracted from user's input
    char szParams[nMaxNumberOfParams][100];     // Command parameters as strings
    dword nParams[nMaxNumberOfParams];          // Command parameters as numbers

    word plci;                                  // Auxvar: Physical Link Connection Identifier
    word ncci;                                  // Auxvar: Network Control Connection Identifier
    byte memBuf[200];                           // Auxvar
    byte *p;                                    // Auxvar
    dword i;                                    // Auxvar

    const char *szInput = sCmdLine.c_str();

    // Parse the input. The input is supposed to be of the format
    //    <command><param0>[,<param1>[,<param2>[,<param3>]]]
    // where <command> is one-character command and <param>s are arbitrary strings separated by commas.
    // szParams and nParams arrays shall be filled with the results of the parsing.

    cCommand = *szInput;
    if ( cCommand == '\0' || cCommand == '\n' )
        return;
    const char *p1 = szInput+1;
    for ( i = 0; i < nMaxNumberOfParams; i++ ) {
        szParams[i][0] = '\0';
        nParams[i] = 0;

        char *p2 = szParams[i];
        while ( *p1 != ',' && *p1 != '\n' && *p1 != '\0' )
            if ( p2-szParams[i] < sizeof(szParams[i]) )      // To avoid buffer overflow
                *p2++ = *p1++;
        *p2 = '\0';
        if ( *p1 == ',' )
            p1++;

        ASSERT( strlen(szParams[i])<sizeof(szParams[i]) ); // Check for buffer overflow

        if ( strstr( szParams[i], "0x" ) == szParams[i] )  // Hex value
            sscanf( szParams[i]+2, "%lx", nParams+i );
        else
            sscanf( szParams[i], "%lu", nParams+i );
    }

    switch ( cCommand )
    {
        case 'm':                                 // Make call
        {
            CMakeCallDialog mcdlg( szParams[0], nParams[1], *szParams[2] == '?' || *szParams[2] == '*' );
            if ( mcdlg.DoModal() != IDOK )
                break;

            ConstructBChannelInfo( memBuf, IsBRI(), mcdlg.m_nBChannel, mcdlg.m_PrefExcl == CMakeCallDialog::bcPref );

            byte *pChannelIdentificationIE = mcdlg.m_PrefExcl != CMakeCallDialog::bcAny ? memBuf : 0;
            byte cTypeAndPlan = 0x80 | ((mcdlg.GetNumberType()&0x07) << 4) | mcdlg.GetNumberingPlanId()&0x0f;

            pcapi->Connect( mcdlg.m_nCIP, (LPCSTR)mcdlg.m_csCalledNumber, cTypeAndPlan, szOwnNumber, pChannelIdentificationIE );
            break;
        }
        case 'a':                                 // Accept call
            ConstructBChannelInfo( memBuf, IsBRI(), nParams[1], *szParams[2] == '?' || *szParams[2] == '*' );

            plci = (word)nParams[0];
            if ( pcapi->IsValidPLCI( plci ) )
                pcapi->conns[plci]->Alert( szParams[1][0] ? memBuf : 0 );
            else
                logout( plogstream(), "Invalid input: use a<plci>[,<B-channel number>[,?]]\n" );
            break;
        case 'c':                                 // Answer call (CONNECT_RESP)
            ConstructBChannelInfo( memBuf, IsBRI(), nParams[1], *szParams[2] == '?' || *szParams[2] == '*' );

            plci = (word)nParams[0];
            if ( pcapi->IsValidPLCI( plci ) )
                pcapi->conns[plci]->ConnectResp( pcapi->wLastConnectionIndXRef, szParams[1][0] ? memBuf : 0 );
            else
                logout( plogstream(), "Invalid input: use c<plci>[,<B-channel number>[,?]]\n" );
            break;
        case 'o':                                 // Overlapped sending of Called Party ID
            plci = (word)nParams[0];
            if ( !pcapi->IsValidPLCI(plci) ) {
                logout( plogstream(), "Invalid PLCI\n" );
                break;
            }
            strcpy( (char*)memBuf+1, szParams[1] );
            memBuf[0] = '\x81';
            pcapi->conns[plci]->Info( (char*)memBuf, 0 );
            break;
        case 'k':                                 // Key in
            plci = (word)nParams[0];
            if ( !pcapi->IsValidPLCI(plci) ) {
                logout( plogstream(), "Invalid PLCI\n" );
                break;
            }
            if ( strlen(szParams[0])==0 || strlen(szParams[1])==0 ) {
                logout( plogstream(), "Invalid input: use k<plci>,<char>{<char>}\n" );
                break;
            }
            p = memBuf;
            *p++ = 4+strlen(szParams[1]);
                *p++ = 0;
                p = CAPI::string2struct( p, szParams[1] );
                *p++ = 0;
                *p++ = 0;
            pcapi->conns[plci]->Info( "", memBuf );
            break;
        case 'd':                                 // Disconnect
            plci = (word)nParams[0];
            if ( pcapi->IsValidPLCI( plci ) )
                pcapi->conns[plci]->Disconnect();
            else
                logout( plogstream(), "Invalid input: use d<plci>\n" );
            break;
        case 'h':                                 // Hold
            plci = (word)nParams[0];
            if ( pcapi->IsValidPLCI( plci ) )
                pcapi->conns[plci]->HoldRetrieve( true );
            else
                logout( plogstream(), "Invalid input: use h<plci>\n" );
            break;
        case 'r':                                 // Retrieve
            plci = (word)nParams[0];
            if ( pcapi->IsValidPLCI( plci ) )
                pcapi->conns[plci]->HoldRetrieve( false );
            else
                logout( plogstream(), "Invalid input: use r<plci>\n" );
            break;
        case 't':                                 // ECT
            plci = (word)nParams[0];
            if ( plci && ((nParams[1] != 0) || (strcmp(szParams[1],"l") == 0)) )
                if ( *szParams[1] == 'l' )
                    pcapi->conns[plci]->ECT_Manually();
                else
                    pcapi->conns[plci]->ECT_ViaCAPI( (word)nParams[1] );
            else
                logout( plogstream(), "Invalid input: use t<plcih>,(<plci>|l)\n" );
            break;
        case 'e':                                 // CD
            plci = (word)nParams[0];
            if ( !pcapi->IsValidPLCI(plci) ) {
                logout( plogstream(), "Invalid PLCI\n" );
                break;
            }
            if ( strlen(szParams[1]) == 0 ) {
                logout( plogstream(), "Invalid input: use e<plci>,<number>\n" );
                break;
            }
            pcapi->conns[plci]->CD_ViaCAPI( szParams[1] );
            break;
        case '(':                                 // Suspend
            plci = (word)nParams[0];
            if ( pcapi->IsValidPLCI(plci) && (strlen(szParams[1]) != 0) )
                pcapi->conns[plci]->SuspendResume( szParams[1], true );
            else
                logout( plogstream(), "Invalid input: use (<plci>,<call identity>\n" );
            break;
        case ')':                                 // Resume
            plci = (word)nParams[0];
            if ( pcapi->IsValidPLCI(plci) && (strlen(szParams[1]) != 0) )
                pcapi->conns[plci]->SuspendResume( szParams[1], false );
            else
                logout( plogstream(), "Invalid input: use )<plci>,<call identity>\n" );
            break;
        case 's':                                 // Arbitrary Supplementary Service
            plci = (word)nParams[0];
            if ( !pcapi->IsValidPLCI(plci) ) {
                logout( plogstream(), "Invalid PLCI\n" );
                break;
            }
            pcapi->conns[plci]->SS_Manually( (byte)nParams[1] );
            break;
        case '~':                                 // ECT LinkIdRequest
            plci = (word)nParams[0];
            if ( !pcapi->IsValidPLCI(plci) ) {
                logout( plogstream(), "Invalid PLCI\n" );
                break;
            }
            pcapi->conns[plci]->EctLinkIdRequest_Manually();
            break;
        case 'f':                                 // Activate/Deactivate/Interrogate call forwarding
            i = strcmp(szParams[0],"u") == 0 ? 0 :
                strcmp(szParams[0],"b") == 0 ? 1 :
                strcmp(szParams[0],"na") == 0 ? 2 : -1;
            if ( i == -1 ) {
                logout( plogstream(), "Invalid input: use f(u|b|na),<forward-from number>,(<fordward-to number>|-|?)\n" );
                break;
            }
            if ( strcmp(szParams[2],"?") == 0 )
                pcapi->CFInterrogate_Manually( (byte)i, szParams[1] );
            else
                pcapi->CF_Manually( (byte)i, szParams[1], (strcmp(szParams[2],"-")==0) ? 0 : szParams[2] );
            break;
        case 'l':                                 // Listen
            if ( strlen(szParams[0]) != 0 )
                pcapi->Listen( (word)nParams[0], strlen(szParams[1])==0 ? 1 : nParams[1] );
            else
                logout( plogstream(), "Invalid input: use l<info mask>[,<CIP mask>]\n" );
            break;
        case 'n':                                 // Network-specific facility
            plci = (word)nParams[0];
            if ( !pcapi->IsValidPLCI(plci) ) {
                logout( plogstream(), "Invalid PLCI\n" );
                break;
            }
            if ( strlen(szParams[1]) == 0 ) {
                logout( plogstream(), "Invalid input: use n<plci>,<char>{<char>}\n" );
                break;
            }
            p = memBuf;
            *p++ = 0x1C;
            *p++ = strlen(szParams[1]) + 1;
            *p++ = 0x00;
            strcpy( (char*)p, szParams[1] );
            pcapi->conns[plci]->Facility_Manually( memBuf, strlen(szParams[1])+3 );
            break;
        case '[':                                 // Connect_B3
            plci = (word)nParams[0];
            if ( pcapi->IsValidPLCI( plci ) )
                pcapi->conns[plci]->ConnectB3();
            else
                logout( plogstream(), "Invalid input: use [<plci>\n" );
            break;
        case ']':                                 // Disconnect_B3
            plci = (word)nParams[0];
            ncci = (word)nParams[1];
            if ( pcapi->IsValidPLCI( plci ) )
                pcapi->conns[plci]->DisconnectB3();
            else
                logout( plogstream(), "Invalid input: use ]<plci>[,<ncci>]\n" );
            break;
        case 'x':                                 // Reset_B3
            plci = (word)nParams[0];
            if ( pcapi->IsValidPLCI( plci ) )
                pcapi->conns[plci]->ResetB3();
            else
                logout( plogstream(), "Invalid input: use x<plci>\n" );
            break;
        case '\'':                                 // Send DTMF digits manually
            plci = (word)nParams[0];
            if ( !pcapi->IsValidPLCI(plci) || !strlen(szParams[1]) ) {
                logout( plogstream(), "Invalid input: use '<plci>,<char>{<char>}\n" );
                break;
            }
            for ( p = (byte *)szParams[1]; *p != '\0'; p++ ) {
                static char *aDigits = "012345678*#";
                char *p1 = strchr( aDigits, *p );
                if ( p1 == 0 )
                    continue;
                byte *pcmData = aDtmf + (p1-aDigits)*nDtmfBufSize/12;
                for ( i = nDtmfBufSize/12/pcapi->maxBDataLen; i > 0; i--, pcmData+=pcapi->maxBDataLen )
                    pcapi->conns[plci]->AdvancedSendData( pcmData, pcapi->maxBDataLen );
                pcapi->conns[plci]->AdvancedSendData( pcmData, nDtmfBufSize/12%pcapi->maxBDataLen );
            }
            break;
        case '\"':                                 // Play file
        {
            plci = (word)nParams[0];
            if ( !pcapi->IsValidPLCI(plci) || !strlen(szParams[1]) ) {
                logout( plogstream(), "Invalid input: use \"<plci>,<filename>\n" );
                break;
            }

            // Open the file

            ifstream is( szParams[1], ios::in | ios::binary );
            if ( !is ) {
                logout( plogstream(), "Cannot open file %s\n", szParams[1] );
                break;
            }

            unsigned char buf[262144], *p;
            is.read( (char*)buf, sizeof buf );
            unsigned long nSize = is.gcount();

            if ( nSize == 0 )
                break;

            for ( i = nSize/pcapi->maxBDataLen, p = buf; i > 0; i--, p+=pcapi->maxBDataLen )
                pcapi->conns[plci]->AdvancedSendData( p, pcapi->maxBDataLen );

            if ( nSize%pcapi->maxBDataLen != 0 )
                pcapi->conns[plci]->AdvancedSendData( p, nSize%pcapi->maxBDataLen );

            break;
        }
        case '>':                                 // Send DTMF digits
            plci = (word)nParams[0];
            if ( pcapi->IsValidPLCI(plci) && strlen(szParams[1]) )
                pcapi->conns[plci]->SendDTMF( szParams[1] );
            else
                logout( plogstream(), "Invalid input: use ><plci>,<char>{<char>}\n" );
            break;
        case '{':                                 // Start Listen to DTMF
            plci = (word)nParams[0];
            if ( pcapi->IsValidPLCI(plci) )
                pcapi->conns[plci]->StartStopDTMFListen( true );
            else
                logout( plogstream(), "Invalid input: use {<plci>\n" );
            break;
        case '}':                                 // Stop Listen to DTMF
            plci = (word)nParams[0];
            if ( pcapi->IsValidPLCI( plci ) )
                pcapi->conns[plci]->StartStopDTMFListen( false );
            else
                logout( plogstream(), "Invalid input: use }<plci>\n" );
            break;
        case '|':                                 // Start/stop wave output
            plci = (word)nParams[0];
            if ( !pcapi->IsValidPLCI( plci ) ) {
                logout( plogstream(), "Invalid PLCI\n" );
                break;
            }
            if ( strcmp( szParams[1], "+" ) == 0 )
                pcapi->conns[plci]->ActivateWavOutput();
            else if ( strcmp( szParams[1], "-" ) == 0 )
                pcapi->conns[plci]->DeactivateWavOutput();
            else
                logout( plogstream(), "Invalid option\n" );
            break;
        case 'z':                                 // Start/stop file output
            plci = (word)nParams[0];
            if ( !pcapi->IsValidPLCI( plci ) ) {
                logout( plogstream(), "Invalid PLCI\n" );
                break;
            }
            if ( strcmp( szParams[1], "" ) == 0 )
                logout( plogstream(), "Filename or '-' must be specified\n" );
            else if ( strcmp( szParams[1], "-" ) == 0 )
                pcapi->conns[plci]->DeactivateRecording();
            else
                pcapi->conns[plci]->ActivateRecording( szParams[1] );
            break;
        case '=':                                 // Line interconnect
        {
            if ( strcmp( szParams[0], "?" ) == 0 ) {
                pcapi->LIGetSupportedServices();
                break;
            }

            word plcim = (word)nParams[0];
            word plcic = (word)nParams[1];

            if ( !pcapi->IsValidPLCI( plcim ) ) {
                logout( plogstream(), "Invalid main PLCI\n" );
                break;
            }

            if ( !pcapi->IsValidPLCI( plcic ) ) {
                logout( plogstream(), "Invalid connected PLCI\n" );
                break;
            }

            if ( strcmp( szParams[2], "+" ) == 0 || strlen(szParams[2]) == 0 )
                pcapi->conns[plcim]->LiConnectSimple( plcic );
            else if ( strcmp( szParams[2], "-" ) == 0 )
                pcapi->conns[plcim]->LiDisconnectSimple( plcic );
            else
                logout( plogstream(), "Invalid third parameter, must be '+', '-', or empty (the same as '+')\n" );

            break;
        }
        case 'g':                                 // Get the list of supported services
            pcapi->GetSupportedServices();
            break;
        case 'i':                                 // Just print out the CAPI information
            if ( nParams[0] == 0 ) {
                // General information
                logout( plogstream(), "Manufacturer:         %s\n", pcapi->szManufacturer );
                logout( plogstream(), "Serial Number:        %s\n", pcapi->szSerialNumber );
                logout( plogstream(), "CAPI Version:         %lu.%lu\n", pcapi->dwCAPIMajor, pcapi->dwCAPIMinor );
                logout( plogstream(), "Manufacturer Version: %lu.%lu\n", pcapi->dwManufacturerMajor, pcapi->dwManufacturerMinor );
                logout( plogstream(), "# of Controllers:     %hu\n", pcapi->wNumberOfControllers );
            }
            else {
                // Information on a specific controller
                if ( pcapi->wNumberOfControllers == 0 ) {
                    logout( plogstream(), "No ISDN controllers found.\n" );
                    break;
                }
                if ( nParams[0] > pcapi->wNumberOfControllers ) {
                    logout( plogstream(), "Invalid controller number.\n" );
                    break;
                }

                CAPIControllerProfile *p = &(pcapi->ControllerProfiles[ nParams[0]-1 ]);
                logout( plogstream(), "NumberOfBChannels:                 %hd\n\n", p->wNumberOfBChannels );

                logout( plogstream(), "SupportInternalController:         %s\n", p->bSupportInternalController ? "+" : "-" );
                logout( plogstream(), "SupportExternalEquipment:          %s\n", p->bSupportExternalEquipment ? "+" : "-" );
                logout( plogstream(), "SupportHandset:                    %s\n", p->bSupportHandset ? "+" : "-" );
                logout( plogstream(), "SupportDTMF:                       %s\n", p->bSupportDTMF ? "+" : "-" );
                logout( plogstream(), "SupportSupplementaryServices:      %s\n", p->bSupportSupplementaryServices ? "+" : "-" );
                logout( plogstream(), "SupportChannelAllocation:          %s\n", p->bSupportChannelAllocation ? "+" : "-" );
                logout( plogstream(), "SupportParameterBChannelOperation: %s\n", p->bSupportParameterBChannelOperation ? "+" : "-" );
                logout( plogstream(), "SupportLineInterconnect:           %s\n\n", p->bSupportLineInterconnect ? "+" : "-" );

                logout( plogstream(), "B1Support64:                       %s\n", p->bB1Support64 ? "+" : "-" );
                logout( plogstream(), "B1Support64BitTransparent:         %s\n", p->bB1Support64BitTransparent ? "+" : "-" );
                logout( plogstream(), "B1SupportV110Asynch:               %s\n", p->bB1SupportV110Asynch ? "+" : "-" );
                logout( plogstream(), "B1SupportV110Synch:                %s\n", p->bB1SupportV110Synch ? "+" : "-" );
                logout( plogstream(), "B1SupportT30:                      %s\n", p->bB1SupportT30 ? "+" : "-" );
                logout( plogstream(), "B1Support64Inverted:               %s\n", p->bB1Support64Inverted ? "+" : "-" );
                logout( plogstream(), "B1Support56BitTransparent:         %s\n", p->bB1Support56BitTransparent ? "+" : "-" );
                logout( plogstream(), "B1SupportModemNegot:               %s\n", p->bB1SupportModemNegot ? "+" : "-" );
                logout( plogstream(), "B1SupportModemAsynch:              %s\n", p->bB1SupportModemAsynch ? "+" : "-" );
                logout( plogstream(), "B1SupportModemSynch:               %s\n\n", p->bB1SupportModemSynch ? "+" : "-" );

                logout( plogstream(), "B2SupportX75:                      %s\n", p->bB2SupportX75 ? "+" : "-" );
                logout( plogstream(), "B2SupportTransparent:              %s\n", p->bB2SupportTransparent ? "+" : "-" );
                logout( plogstream(), "B2SupportSDLC:                     %s\n", p->bB2SupportSDLC ? "+" : "-" );
                logout( plogstream(), "B2SupportQ921:                     %s\n", p->bB2SupportQ921 ? "+" : "-" );
                logout( plogstream(), "B2SupportT30:                      %s\n", p->bB2SupportT30 ? "+" : "-" );
                logout( plogstream(), "B2SupportPPP:                      %s\n", p->bB2SupportPPP ? "+" : "-" );
                logout( plogstream(), "B2SupportTransparentIgnoreB1:      %s\n", p->bB2SupportTransparentIgnoreB1 ? "+" : "-" );
                logout( plogstream(), "B2SupportModemErrCorrCompr:        %s\n", p->bB2SupportModemErrCorrCompr ? "+" : "-" );
                logout( plogstream(), "B2SupportX75V42bis:                %s\n", p->bB2SupportX75V42bis ? "+" : "-" );
                logout( plogstream(), "B2SupportV120Asynch:               %s\n", p->bB2SupportV120Asynch ? "+" : "-" );
                logout( plogstream(), "B2SupportV120AsynchV42bis:         %s\n", p->bB2SupportV120AsynchV42bis ? "+" : "-" );
                logout( plogstream(), "B2SupportV120BitTransparent:       %s\n", p->bB2SupportV120BitTransparent ? "+" : "-" );
                logout( plogstream(), "B2SupportQ921SAPISelection:        %s\n\n", p->bB2SupportQ921SAPISelection ? "+" : "-" );

                logout( plogstream(), "B3SupportTransparent:              %s\n", p->bB3SupportTransparent ? "+" : "-" );
                logout( plogstream(), "B3SupportT90:                      %s\n", p->bB3SupportT90 ? "+" : "-" );
                logout( plogstream(), "B3SupportISO8208:                  %s\n", p->bB3SupportISO8208 ? "+" : "-" );
                logout( plogstream(), "B3SupportX25:                      %s\n", p->bB3SupportX25 ? "+" : "-" );
                logout( plogstream(), "B3SupportT30:                      %s\n", p->bB3SupportT30 ? "+" : "-" );
                logout( plogstream(), "B3SupportT30Ext:                   %s\n", p->bB3SupportT30Ext ? "+" : "-" );
                logout( plogstream(), "B3SupportModem:                    %s\n\n", p->bB3SupportModem ? "+" : "-" );

                logout( plogstream(), "CAPIInternalInformation:           %s\n", p->memCAPIInternalInformation );
                logout( plogstream(), "ManufacturerSpecificInformation:   %s\n", p->memManufacturerSpecificInformation );
            }
            break;
        case '@':
            pcapi->ConnectDChannel( (word)nParams[0] );
            break;
        case '%':
        {
            plci = (word)nParams[0];
            if ( !pcapi->IsValidPLCI(plci) ) {
                logout( plogstream(), "Invalid PLCI\n" );
                break;
            }
            if ( strlen(szParams[1]) == 0 ) {
                logout( plogstream(), "Empty command name\n" );
                break;
            }

            string sFormat = RegWrap( cszRegKey ).ReadString( szParams[1] );

            if ( sFormat.empty() ) {
                logout( plogstream(), "Empty or non-existing command\n" );
                break;
            }

            const char * const szFormat = sFormat.c_str();
            byte *pBuf = memBuf;
            for ( const char *p = szFormat; *p != '\0'; )
            {
                if ( *p == '\\' ) {
                    ++p;
                    if ( *p != 'x' && *p != 'X' ) {
                        logout( plogstream(), "Invalid escape sequence in command\n" );
                        break;
                    }
                    ++p;
                    byte s = 0;
                    while ( *p != 0 && strchr( "0123456789abcdefABCDEF", *p ) != 0 ) {
                        byte val = *p - 0x30;
                        if ( val > 15 )
                            val -= 0x07;
                        if ( val > 15 )
                            val -= 0x20;
                        s = s*16 + val;
                        ++p;
                    }
                    *pBuf++ = s;
                }
                else {
                    *pBuf++ = *p++;
                }
            }

            pcapi->conns[plci]->AdvancedSendData( memBuf, pBuf - memBuf );
            break;
        }
        case '&':
            pcapi->ConnectLeased( nParams[0] );
            break;
        case 'b':
            plci = (word)nParams[0];
            if ( !pcapi->IsValidPLCI(plci) ) {
                logout( plogstream(), "Invalid PLCI\n" );
                break;
            }
            if ( strcmp(szParams[1],"d") == 0 )
                pcapi->conns[plci]->SelectBProtocol( (byte*)(memBProtocolRaw[0]) );
            else if ( strcmp(szParams[1],"b") == 0 )
                pcapi->conns[plci]->SelectBProtocol( (byte*)memBProtocol64 );
            else if ( strcmp(szParams[1],"s") == 0 )
                pcapi->conns[plci]->SelectBProtocol( (byte*)memBProtocolFaxSend );
            else if ( strcmp(szParams[1],"r") == 0 )
                pcapi->conns[plci]->SelectBProtocol( (byte*)memBProtocolFaxRcv );
            else
                logout( plogstream(), "Invalid choice\n" );
            break;
        case '#':                                 // Setting and displaying our number
            if ( szParams[0][0] == '\0' )
                // Display the current number
                logout( plogstream(), "%s\n", (szOwnNumber==0) || (szOwnNumber[0]=='\0') ? "No number assigned" : szOwnNumber );
            else if ( strcmp(szParams[0],"-") == 0 ) {
                // Erase the number
                szOwnNumber[0] = '\0';
                logout( plogstream(), "The number has been erased.\n" );
            }
            else {
                // Assign a new number
                if ( strlen(szParams[0]) < 20 ) {
                    strcpy( szOwnNumber, szParams[0] );
                    logout( plogstream(), "Number %s assigned.\n", szOwnNumber );
                }
                else
                    logout( plogstream(), "Number length cannot exceed 20 symbols.\n" );
            }
            break;
        case 'q':                                 // Quit
            break;
        case '?':                                 // Display help
        default:
            logout( plogstream(), "Commands:\n"
                    "\tm[<number>[,<B-channel number>[,?]] - Make a call\n"
                    "\ta<plci>[,<B-channel number>[,?]] - Accept a call (ALERT_REQ)\n"
                    "\tc<plci>[,<B-channel number>[,?]] - Answer a call (CONNECT_RESP)\n"
                    "\to<plci>,<char>{<char>} - Overlapped Sending\n"
                    "\tk<plci>,<char>{<char>} - Keypad facility\n"
                    "\td<plci> - Disconnect\n"
                    "\th<plci> - Hold\n"
                    "\tr<plci> - Retrieve\n"
                    "\tt<plcih>,(<plci>|l) - ECT\n"
                    "\te<plci>,<number> - CD\n"
                    "\t(<plci>,<call identity> - Suspend\n"
                    "\t)<plci>,<call identity> - Resume\n"
                    "\ts<plci>,<operation> - Arbitrary Supplementary Service\n"
                    "\t~<plci> - EctLinkIdRequest\n"
                    "\tf(u|b|na),<forward-from number>,(<fordward-to number>|-|?) - Activate/Deactivate/Interrogate call forwarding\n"
                    "\tl<info mask>[,<CIP mask>] - Listen\n"
                    "\t-------------------------------\n"
                    "\tn<plci>,<char>{<char>} - Network-specific facility\n"
                    "\t-------------------------------\n"
                    "\t[<plci> - B3 Connect\n"
                    "\t]<plci>[,<ncci>] - B3 Disconnect\n"
                    "\tx<plci> - Reset B3\n"
                    "\t]<plci>[,<ncci>] - B3 Disconnect\n"
                    "\t'<plci>,<char>{<char>} - Send DTMF digits manually\n"
                    "\t{<plci> - Start DTMF listen\n"
                    "\t}<plci> - Stop DTMF listen\n"
                    "\tb<plci>,(d|b|s|r) - Select B Protocol\n"
                    "\t\"<plci>,<filename> - Send file\n"
                    "\t|<plci>,(+|-) - Activate/deactivate playing audio stream of the connection\n"
                    "\tz<plci>,(<filename>|-) - Activate/deactivate recording of incoming B-channel data into a file\n"
                    "\t=(?|<plcim>,<plcip>[,(+|-)]) - Line Interconnect: get supported services/connect/disconnect\n"
                    "\t-------------------------------\n"
                    "\t@[<index>] - Set up direct D channel access (see sources about the meaning of the index parameter)\n"
                    "\t%%<plci>,<byte sequence name> - Reads the specified byte sequence from registry and sends as data (for use with @)\n"
                    "\t-------------------------------\n"
                    "\t&(0|<B-channel number>) - Connect leased\n"
                    "\t-------------------------------\n"
                    "\t#         - Display our number (the one we send as Calling Party Number)\n"
                    "\t#<number> - Set our number (to send as Calling Party Number)\n"
                    "\t#-        - Erase our number (Calling Party Number IE will not be sent)\n"
                    "\t-------------------------------\n"
                    "\ti[<controller>] - CAPI Information\n"
                    "\tg - Get supported services\n"
                    "\tq - Quit\n"
                    "\t? - This help screen\n" );
            break;
    }
}

void CCapiConApp::OnEditCut()
{
    ((CMainFrame*)m_pMainWnd)->m_pMainForm->m_Log.SetWindowText( "" );
}
